<?php

namespace App\Common\Redis;

use FriendsOfHyperf\Lock\Exception\LockTimeoutException;
use Hyperf\Coroutine\Parallel;
use Hyperf\Redis\Redis;
use function FriendsOfHyperf\Lock\lock;
use function Hyperf\Support\optional;

class RedisCluster
{
    /**
     * @param string $key
     * @param int $parallelLimit 最大运行的协程数，比如我们希望最大设置的协程数为 5 ，
     * 也就意味着 Parallel 里最多只会有 5 个协程在运行，
     * 只有当 5 个里有协程完成结束后，后续的协程才会继续启动，直至所有协程完成任务
     * @return array
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function scan(string $key, int $parallelLimit = 5): array
    {
        $redisPrefix = env('REDIS_PREFIX');
        $cachePrefix = config('cache.default.prefix');
        $searchPattern = $redisPrefix . $cachePrefix . $key;

        $redis = container()->get(Redis::class);

        $lock = lock('redis_cluster_scan',10);
        try {
            $lock->block(5);
            $keys = [];
            $scanCount = 1000;
            if (env('REDIS_CLUSTER_ENABLE')) {
                $parallel = new Parallel($parallelLimit);
                foreach ($redis->_masters() as $item) {
                    $parallel->add(function () use ($redis, $item, $searchPattern, $scanCount) {
                        $iterator = null;
                        $nodeKeys = [];
                        while (false !== ($result = $redis->scan($iterator, $item, $searchPattern, $scanCount))) {
                            if (!empty($result)) {
                                $nodeKeys = array_merge($nodeKeys, $result);
                            }
                        }
                        return $nodeKeys;
                    });
                }
                $results = $parallel->wait();
                foreach ($results as $result) {
                    $keys = array_merge($keys, $result);
                }
            } else {
                $iterator = null;
                while (false !== ($configKey = $redis->scan($iterator, $searchPattern, $scanCount))) {
                    $keys = array_merge($keys, $configKey);
                }
            }

            foreach ($keys as $k=>$item){
                $keys[$k] = str_replace($redisPrefix,'',$item);
            }
            return $keys;
        } catch (LockTimeoutException $e) {
            // 无法获取锁..
            return [];
        } finally {
            optional($lock)->release();
        }
    }
}